package com.netflexitysolutions.amazonws.ec2.internal.operations;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import com.amazonaws.ec2.doc._2009_04_04.BundleInstanceResponseType;
import com.amazonaws.ec2.doc._2009_04_04.BundleInstanceS3StorageType;
import com.amazonaws.ec2.doc._2009_04_04.BundleInstanceTaskStorageType;
import com.amazonaws.ec2.doc._2009_04_04.BundleInstanceType;
import com.netflexitysolutions.amazonws.ec2.BundleInstanceInfo;
import com.netflexitysolutions.amazonws.ec2.EC2;
import com.netflexitysolutions.amazonws.ec2.internal.ModelUtil;

public class BundleInstanceOperation extends SingleObjectOperationExecutor<BundleInstanceType, BundleInstanceInfo> {

	private String bucketId;
	
	private String prefix;
	
	private String awsAccessKeyId;
	
	private int minutesToExpiration = 720;
	
	private String acl = "ec2-bundle-read";
	
	private String awsSecretKey;
	
	private String algorithm = "HmacSHA1";
	
	public BundleInstanceOperation(EC2 ec2, String instanceId, String bucketId, String prefix, String awsAccessKeyId, 
			String awsSecretKey) {
		super(ec2, instanceId);
		this.bucketId = bucketId;
		this.prefix = prefix;
		this.awsAccessKeyId = awsAccessKeyId;
		this.awsSecretKey = awsSecretKey;
	}

	@Override
	protected BundleInstanceInfo call(BundleInstanceType request) {
		request.setInstanceId(objectId);
		request.setStorage(createStorageElement());
		BundleInstanceResponseType response = getEC2().getService().bundleInstance(request);
		return ModelUtil.toDomainType(response.getBundleInstanceTask());
	}
	
	private BundleInstanceTaskStorageType createStorageElement() {
		BundleInstanceTaskStorageType type = new BundleInstanceTaskStorageType();
		BundleInstanceS3StorageType s3 = new BundleInstanceS3StorageType();
		type.setS3(s3);
		s3.setAwsAccessKeyId(awsAccessKeyId);
		s3.setBucket(bucketId);
		s3.setPrefix(prefix);
		s3.setUploadPolicy(createPolicy());
		s3.setUploadPolicySignature(createSignature(s3.getUploadPolicy()));
		return type;
	}
	
	private String createPolicy() {
		StringBuilder json = new StringBuilder("{\n");
		json.append("\"expiration\": \"");
        final String DateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'";
        SimpleDateFormat format = new SimpleDateFormat( DateFormat, Locale.US );
        format.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
		json.append(format.format(new Date(System.currentTimeMillis()+(minutesToExpiration*60000L))));
		json.append("\",\n");
		json.append("\"conditions\": [\n");

		json.append("{\"acl\": \"");
		json.append(acl);
		json.append("\"},\n");

		json.append("{\"bucket\": \"");
		json.append(bucketId);
		json.append("\"},\n");

		json.append("]\n}");
		return new String(Base64.encodeBase64(json.toString().getBytes()));
	}
	
	private String createSignature(String policy) {
        SecretKeySpec signingKey =
            new SecretKeySpec(awsSecretKey.getBytes(), algorithm);

        // Acquire the MAC instance and initialize with the signing key.
		Mac mac = null;
		try {
			mac = Mac.getInstance(algorithm);
		} catch (NoSuchAlgorithmException e) {
			// should not happen
			throw new RuntimeException("Could not find sha1 algorithm", e);
		}
		try {
			mac.init(signingKey);
		} catch (InvalidKeyException e) {
			// also should not happen
			mac = null;
			throw new RuntimeException("Could not initialize the MAC algorithm", e);
		}

        // Compute the HMAC on the digest, and set it.
		byte [] signedBytes = null;
		synchronized (mac) {
			try {
				signedBytes = mac.doFinal(policy.getBytes("UTF-8"));
			} catch (UnsupportedEncodingException e) {
				signedBytes = mac.doFinal(policy.getBytes());
			}
		}
        return new String(Base64.encodeBase64(signedBytes));
	}
}
